from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.cluster import AgglomerativeClustering
from kneed import KneeLocator
import plotly.graph_objects as go

class support():
    """
    支撑阻力位计算
    """

    def clustering(self, highArr, supportSize=20):
        """
        层次聚类算法，也叫凝聚聚类
        supportSize   阻力位数量
        """

        df = pd.DataFrame(highArr)
        max = df.rolling(10).max()
        max.rename(columns={0: "price"}, inplace=True)
        min = df.rolling(10).min()
        min.rename(columns={0: "price"}, inplace=True)

        maxdf = pd.concat([max, pd.Series(np.zeros(len(max)) + 1)], axis=1)
        mindf = pd.concat([min, pd.Series(np.zeros(len(min)) + -1)], axis=1)
        maxdf.drop_duplicates('price', inplace=True)
        mindf.drop_duplicates('price', inplace=True)

        buffArr = pd.concat([maxdf,mindf])
        F = buffArr.sort_index()
        F = F[F[0] != F[0].shift()].dropna()

        # Create [x,y] array where y is always 1
        X = np.concatenate((F.price.values.reshape(-1, 1), (np.zeros(len(F)) + 1).reshape(-1, 1)), axis=1)

        cluster = AgglomerativeClustering(n_clusters=int(supportSize), affinity='euclidean', linkage='ward')
        cluster.fit_predict(X)
        F['clusters'] = cluster.labels_

        F2 = F.loc[F.groupby('clusters')['price'].idxmax()]

        # Plotit
        # fig, axis = plt.subplots()
        # supportArr = F2.itertuples()

        result = []
        value = ""
        for row in F2.itertuples():
            # axis.axhline(y=row.price, color='green', ls='dashed')
            result.append(int(row.price))
        # axis.plot(F.index.values, F.price.values)
        # plt.show()
        result.sort(reverse=True)

        for item in result:
            if item != "":
                value = value + str(item) + ","
        return value

    def Kmeans(self,df,threshold = 3):
        """
        K-means聚类计算股票支撑位和阻力位
        threshold  阻力間隔
        """
        sum_of_sq_distances = []
        X = np.array(df["close"])
        K = range(1, 10)
        for k in K:
            km = KMeans(n_clusters=k)
            km = km.fit(X.reshape(-1, 1))
            sum_of_sq_distances.append(km.inertia_)
        kn = KneeLocator(K, sum_of_sq_distances, S=1.0, curve="convex", direction="decreasing")
        print(f'拐点所在的x轴是: {kn.elbow}')
        kn.plot_knee()

        kmeans = KMeans(n_clusters=kn.knee).fit(X.reshape(-1, 1))
        c = kmeans.predict(X.reshape(-1, 1))
        min_and_max = []
        for i in range(kn.knee):
            min_and_max.append([-np.inf, np.inf])
        for i in range(len(X)):
            cluster = c[i]
            if X[i] > min_and_max[cluster][0]:
                min_and_max[cluster][0] = X[i]
            if X[i] < min_and_max[cluster][1]:
                min_and_max[cluster][1] = X[i]
        # self.show(df,min_and_max)

        valueArr=[]
        for item in min_and_max:
            valueArr=valueArr+item
        result=self.redundance(valueArr,threshold)
        print("Sorted Min/Max Values:\n", result)


    def KmeansFix(self,df):
        """
        K-means聚类计算股票支撑位和阻力位
        """

        df_prices = np.array(df["close"])
        K = 6
        kmeans = KMeans(n_clusters=K).fit(df_prices.reshape(-1, 1))
        clusters = kmeans.predict(df_prices.reshape(-1, 1))
        pd.options.plotting.backend = 'plotly'

        min_max_values = []
        for i in range(K):
            min_max_values.append([np.inf, -np.inf])

        for i in range(len(df_prices)):
            cluster = clusters[i]
            if df_prices[i] < min_max_values[cluster][0]:
                min_max_values[cluster][0] = df_prices[i]
            if df_prices[i] > min_max_values[cluster][1]:
                min_max_values[cluster][1] = df_prices[i]
        
        output = []
        s = sorted(min_max_values, key=lambda x: x[0])
        for i, (_min, _max) in enumerate(s):
            if i == 0:
                output.append(_min)
            if i == len(min_max_values) - 1:
                output.append(_max)
            else:
                output.append(sum([_max, s[i+1][0]]) / 2)
        # self.show(df,output)
        return output

    def redundance(self,res,threshold=3):
        '''
        去除冗餘數據
        '''
        result = []
        for i, value in enumerate(res):
            if i == 0:
                result.append(value)
            else:
                min_distance = min(abs(value - result[j]) for j in range(len(result)))
                if min_distance >= threshold:
                    result.append(value)
        return result

    def show(self,df,output):
        fig = df.plot.scatter(x=df.index,y="close",color="red", )
        for cluster_avg in output[1:-1]:
            fig.add_hline(y=cluster_avg, line_width=1, line_color="blue")
        fig.add_trace(go.Trace(x=df.index,y=df['close'],line_color="black",line_width=1))
        layout = go.Layout(plot_bgcolor='#efefef',showlegend=False,font_family='Monospace',
            font_color='#000000',font_size=20,xaxis=dict(rangeslider=dict(visible=False)))
        fig.update_layout(layout)
        fig.show()